package org.banyan.senior.idempotent.util;

import cn.hutool.core.util.RandomUtil;
import org.banyan.senior.idempotent.bean.AipToken;
import org.banyan.senior.idempotent.bean.MyHttpServletRequestWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @author metalsbanyan
 * @version 1.0
 * @description:
 * @date 2023/7/21 17:10
 */
@Component
public class RedisTokenUtil implements BeanFactoryAware {
    public static final String USER_TOKEN = "userToken";
    public static final String API_TOKEN = "apiToken";
    public static final String API_SIGN = "apiSign";
    private static StringRedisTemplate stringRedisTemplate;
    private static ValueOperations<String, String> operations;

    public static String getApiToken(HttpServletRequest httpServletRequest) {
        AipToken aipToken = AipToken.builder()
                .apiSign(httpServletRequest.getHeader(API_SIGN))
                .generateToken(RandomUtil.randomString(100))
                .userToken(httpServletRequest.getHeader(USER_TOKEN))
                .build();
        String apiToken = aipToken.getApiToken();
        operations.set(apiToken, apiToken);
        return apiToken;
    }

    private static String getAllParams(HttpServletRequest httpServletRequest) throws IOException {
        MyHttpServletRequestWrapper requestWrapper = (MyHttpServletRequestWrapper) httpServletRequest;
        Map<String, String[]> parameterMap = httpServletRequest.getParameterMap();
        StringBuilder params = new StringBuilder();
        parameterMap.forEach((k, v) -> {
            params.append("'");
            params.append(k);
            params.append("'");
            params.append(":");
            params.append(Arrays.toString(v));
        });
        String body = Optional.ofNullable(requestWrapper.getBody()).orElse("");
        return params.append(body).toString();
    }

    /**
     * 幂等性校验
     */
    public static boolean validateApiToken(HttpServletRequest httpServletRequest, String apiToken) throws IOException {
        String userToken = httpServletRequest.getHeader(USER_TOKEN);
        String methodName = httpServletRequest.getRequestURI();
        String recomputeApiToken = AipToken.builder()
                .apiSign(methodName)
                .generateToken(getAllParams(httpServletRequest))
                .userToken(userToken)
                .build()
                .getApiToken();
        // 如果相等，则说明重复的请求
        return recomputeApiToken.equals(apiToken);
    }


    /**
     * 是否重复点击
     * 防止多次点击, 一定时间内不允许同一用户重复请求某个接口
     */
    public static boolean isDuplicateRequest(HttpServletRequest httpServletRequest) {
        String userToken = httpServletRequest.getHeader(USER_TOKEN);
        String methodName = httpServletRequest.getRequestURI();
        String apiToken = userToken + methodName;
        return operations.get(apiToken) != null;
    }

    /**
     * @param httpServletRequest
     */
    public static void setDuplicateRequestCache(HttpServletRequest httpServletRequest, long duplicateRequestTimes) {
        String userToken = httpServletRequest.getHeader(USER_TOKEN);
        String methodName = httpServletRequest.getRequestURI();
        String apiToken = userToken + methodName;
        operations.set(apiToken, apiToken, duplicateRequestTimes, TimeUnit.MILLISECONDS);
    }

    public static void setApiToken(HttpServletRequest httpServletRequest) throws IOException {
        String userToken = httpServletRequest.getHeader(USER_TOKEN);
        String methodName = httpServletRequest.getRequestURI();
        String apiToken = AipToken.builder()
                .apiSign(methodName)
                .generateToken(getAllParams(httpServletRequest))
                .userToken(userToken)
                .build()
                .getApiToken();
        if (apiToken !=null){
            operations.set(httpServletRequest.getHeader(API_TOKEN), apiToken);
        }
    }


    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        RedisTokenUtil.stringRedisTemplate = beanFactory.getBean(StringRedisTemplate.class);
        RedisTokenUtil.operations = RedisTokenUtil.stringRedisTemplate.opsForValue();
    }

    public static ValueOperations<String, String> getOperations() {
        return operations;
    }
}
